﻿using System;
using System.Collections.Generic;
using System.Text;


namespace CalibrationLibrary
{
    
    public class Parameter
    {
        static Random random = new Random();
        double ShaveFraction;
        public Domain domain;
        private Domain original_domain;
        List<double> values;
        List<double> probabilities;
        double runningvalue;
        double LastAcceptedValue;
        string label;
        public string Label
        {
            get
            {
                return label;
            }
        }
        public static void SetRandomSeed(int seed)
        {
            random = new Random(seed);
        }
        public double RunningValue
        {
            get
            {
                return runningvalue;
            }
            set
            {
                runningvalue = value;
            }
        }
        double Min(List<double> v)
        {
            double min = double.MaxValue;
            foreach (double _v in v) if (_v <min ) min = _v;
            return min;
        }
        double Max(List<double> v)
        {
            double max = double.MinValue;
            foreach(double _v in v)if(_v > max)max = _v;
            return max;
        }
        public double Convergence
        {
            get
            {
                if (original_domain.Range > 0) return domain.Range / original_domain.Range;
                else return 1;
            }
        }

        public void AdjustRange()
        {
            RankAccordingTo.Rank(ref values, ref probabilities);

            List<double> AcceptedValues = new List<double>(values);

            int index = (int)Math.Round(AcceptedValues.Count * ShaveFraction, 0);

            if (values.Count - index <= 1) return;

            int FirstAccepted = AcceptedValues.Count - index;
            AcceptedValues.RemoveRange(FirstAccepted, AcceptedValues.Count - FirstAccepted);
            probabilities.RemoveRange(FirstAccepted, probabilities.Count - FirstAccepted);

            domain = new Domain(Min(AcceptedValues), Max(AcceptedValues));

            values = new List<double>(AcceptedValues);
             
            if(Convergence>1)
            {
                throw new System.Exception("Convergence = " + Convergence +" for "+Label);
            }
        }

        public void UseLastAcceptedValue()
        {
            runningvalue = LastAcceptedValue;
        }
        public void AcceptRunningValue()
        {
            LastAcceptedValue = runningvalue;
        }
        public bool OutOfRange()
        {
            bool ToLarge = (runningvalue > domain.Max ? true : false);
            bool ToSmall = (runningvalue < domain.Min ? true : false);
            return (ToLarge || ToSmall ? true : false);
        }

        public void Jump(double FractionOfDomain)
        {
            runningvalue = LastAcceptedValue + (random.NextDouble() - 0.5F) * FractionOfDomain * domain.Range;

            /*
            while (OutOfRange())
            {
                double ToLarge = Math.Max(0, runningvalue - domain.Max);
                if (ToLarge>0) runningvalue = domain.Max - ToLarge;

                double ToSmall = Math.Max(0, domain.Min - runningvalue);
                if (ToSmall > 0) runningvalue = domain.Min + ToSmall;
            }
             */
        }
        public void RandomJump()
        {
            runningvalue = domain.Min + random.NextDouble() * domain.Range;
        }
        public void AddValueAndProbability(double probability)
        {
            values.Add(runningvalue);
            probabilities.Add(probability);

           
        }
        public Parameter(Parameter p)
        {
            this.label = p.label;
            domain = new Domain(p.domain.Min, p.domain.Max);
            original_domain = new Domain(p.original_domain.Min, p.original_domain.Max);

            values = new List<double>();
            probabilities = new List<double>();
        }
        public Parameter(string label, double Min, double Max, double ShaveFraction)
        {
            // TODO get rid of  ShaveFraction
            this.ShaveFraction = ShaveFraction;
            this.label = label;
            domain = new Domain(Min, Max);
            original_domain = new Domain(domain.Min, domain.Max);
 
            values = new List<double>();
            probabilities = new List<double>();

            // Take a random value within the domain
            RandomJump();
            LastAcceptedValue = runningvalue;
        }
    }
}
